From 1c894096e7b32c7ad87d36bb2114bcfcd8fe759b Mon Sep 17 00:00:00 2001 From: "mjw@wray-m-3.hpl.hp.com" Date: Mon, 5 Jul 2004 16:19:14 +0000 Subject: [PATCH] bitkeeper revision 1.1041.5.5 (40e97f82YUOF-rGuIapKOE73WUHZcg) Add control over domain restart on shutdown. --- tools/examples/xmdefaults | 3 +- tools/examples/xmnetbsd | 29 +++-- tools/python/xen/xend/XendClient.py | 5 +- tools/python/xen/xend/XendDomain.py | 141 ++++++++++++++++++++-- tools/python/xen/xend/XendDomainInfo.py | 4 + tools/python/xen/xend/server/SrvDomain.py | 6 +- tools/python/xen/xm/create.py | 6 + tools/python/xen/xm/shutdown.py | 35 +++++- 8 files changed, 193 insertions(+), 36 deletions(-) diff --git a/tools/examples/xmdefaults b/tools/examples/xmdefaults index cf9e9861db..640a7390a0 100644 --- a/tools/examples/xmdefaults +++ b/tools/examples/xmdefaults @@ -2,7 +2,6 @@ #============================================================================ # Python defaults setup for 'xm create'. # Edit this file to reflect the configuration of your system. - #============================================================================ # Define script variables here. @@ -94,6 +93,6 @@ extra = "4 VMID=%d usr=/dev/sda6" % vmid #---------------------------------------------------------------------------- # Set according to whether you want the domain restarted when it exits. # The default is False. -#restart = True +#autorestart = True #============================================================================ diff --git a/tools/examples/xmnetbsd b/tools/examples/xmnetbsd index 0563239464..4f920087b6 100644 --- a/tools/examples/xmnetbsd +++ b/tools/examples/xmnetbsd @@ -2,24 +2,23 @@ #============================================================================ # Python defaults setup for 'xm create'. # Edit this file to reflect the configuration of your system. -# This file expects the variable 'vmid' to be set. #============================================================================ -def config_usage (): - print >>sys.stderr,""" -The config file '%s' requires the following variable to be defined: - vmid -- Numeric identifier for the new domain, used to calculate - the VM's IP address and root partition. E.g. -Dvmid=1 -""" % config_file +# Define script variables here. +# xm_vars is defined automatically, use xm_vars.var() to define a variable. +def vmid_check(var, val): + val = int(val) + if val <= 0: + raise ValueError + return val + +xm_vars.var('vmid', + use="Virtual machine id. Integer greater than 0.", + check=vmid_check) -try: - vmid = int(vmid) # convert to integer -except: - raise ValueError, "Variable 'vmid' must be an integer" - -if vmid <= 0: - raise ValueError, "Variable 'vmid' must be greater than 0" +# This checks the script variables. +xm_vars.check() #---------------------------------------------------------------------------- # Kernel image file. @@ -97,6 +96,6 @@ extra = "4 VMID=%d bootdev=xennet0" % vmid #---------------------------------------------------------------------------- # Set according to whether you want the domain restarted when it exits. # The default is False. -#restart = True +#autorestart = True #============================================================================ diff --git a/tools/python/xen/xend/XendClient.py b/tools/python/xen/xend/XendClient.py index 12a21c2c9c..b5d57b7174 100644 --- a/tools/python/xen/xend/XendClient.py +++ b/tools/python/xen/xend/XendClient.py @@ -225,9 +225,10 @@ class Xend: return xend_call(self.domainurl(id), {'op' : 'pause'}) - def xend_domain_shutdown(self, id): + def xend_domain_shutdown(self, id, reason): return xend_call(self.domainurl(id), - {'op' : 'shutdown'}) + {'op' : 'shutdown', + 'reason' : reason }) def xend_domain_destroy(self, id): return xend_call(self.domainurl(id), diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index 328450a6b5..4bde519b49 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -5,8 +5,10 @@ Needs to be persistent for one uptime. """ import sys +import traceback from twisted.internet import defer +from twisted.internet import reactor import xen.lowlevel.xc; xc = xen.lowlevel.xc.new() @@ -28,10 +30,19 @@ __all__ = [ "XendDomain" ] class XendDomain: """Index of all domains. Singleton. """ - + + """Path to domain database.""" dbpath = "domain" + + """Table of domain info indexed by domain id.""" domain = {} + """Table of configs for domain restart, indexed by domain id.""" + restarts = {} + + """Table of delayed calls.""" + schedule = {} + def __init__(self): self.xconsole = XendConsole.instance() # Table of domain info indexed by domain id. @@ -49,6 +60,53 @@ class XendDomain: print 'XendDomain> virq', val self.reap() + def schedule_later(self, _delay, _name, _fn, *args): + """Schedule a function to be called later (if not already scheduled). + + _delay delay in seconds + _name schedule name + _fn function + args arguments + """ + if self.schedule.get(_name): return + self.schedule[_name] = reactor.callLater(_delay, _fn, *args) + + def schedule_cancel(self, name): + """Cancel a scheduled function call. + + name schedule name to cancel + """ + callid = self.schedule.get(name) + if not callid: + return + if callid.active(): + callid.cancel() + del self.schedule[name] + + def reap_schedule(self, delay=0): + """Schedule reap to be called later. + + delay delay in seconds + """ + self.schedule_later(delay, 'reap', self.reap) + + def reap_cancel(self): + """Cancel any scheduled reap. + """ + self.schedule_cancel('reap') + + def refresh_schedule(self, delay=0): + """Schedule refresh to be called later. + + delay delay in seconds + """ + self.schedule_later(delay, 'refresh', self.refresh) + + def refresh_cancel(self): + """Cancel any scheduled refresh. + """ + self.schedule_cancel('refresh') + def rm_all(self): """Remove all domain info. Used after reboot. """ @@ -145,6 +203,7 @@ class XendDomain: """Look for domains that have crashed or stopped. Tidy them up. """ + self.reap_cancel() print 'XendDomain>reap>' domlist = xc.domain_getinfo() casualties = [] @@ -158,12 +217,14 @@ class XendDomain: for d in casualties: id = str(d['dom']) print 'XendDomain>reap> died id=', id, d - self.domain_destroy(id, refresh=0) + self.final_domain_destroy(id) print 'XendDomain>reap<' def refresh(self): """Refresh domain list from Xen. """ + self.refresh_cancel() + print 'XendDomain>refresh>' domlist = xc.domain_getinfo() # Index the domlist by id. # Add entries for any domains we don't know about. @@ -184,7 +245,7 @@ class XendDomain: d.update(dominfo) else: self._delete_domain(d.id) - self.reap() + self.reap_schedule(1) def refresh_domain(self, id): """Refresh information for a single domain. @@ -267,21 +328,70 @@ class XendDomain: """Shutdown domain (nicely). id domain id - reason shutdown type: poweroff, reboot, halt + reason shutdown type: poweroff, reboot, suspend, halt """ dom = int(id) if dom <= 0: return 0 + self.domain_restart_schedule(id, reason) eserver.inject('xend.domain.shutdown', [id, reason]) + if reason == 'halt': + reason = 'poweroff' val = xend.domain_shutdown(dom, reason) - self.refresh() + self.refresh_schedule() return val - - def domain_destroy(self, id, refresh=1): - """Terminate domain immediately. + + def domain_restart_schedule(self, id, reason): + """Schedule a restart for a domain if it needs one. + + id domain id + reason shutdown reason + """ + if id in self.restarts: + # Don't schedule if already there. + return + restart = 0 + if reason in ['poweroff', 'reboot']: + dominfo = self.domain.get(id) + if dominfo and (dominfo.autorestart or reason == 'reboot'): + restart = 1 + # Clear autorestart flag to avoid multiple restarts. + dominfo.autorestart = 0 + + if restart: + self.restarts[id] = dominfo.config + + def domain_restart_cancel(self, id): + """Cancel any restart scheduled for a domain. + + id domain id + """ + dominfo = self.domain.get(id) + if dominfo: + dominfo.autorestart = 0 + if id in self.restarts: + del self.restarts[id] + + def domain_restarts(self): + """Execute any scheduled domain restarts for domains that have gone. + """ + for id in self.restarts.keys(): + if id in self.domain: + # Don't execute restart for domains still running. + continue + config = self.restarts[id] + # Remove it from the restarts. + del self.restarts[id] + try: + self.domain_create(config) + except: + print >>sys.stderr, "XendDomain> Exception restarting domain" + traceback.print_exc(sys.stderr) + + def final_domain_destroy(self, id): + """Final destruction of a domain.. id domain id - refresh send a domain destroy event if true """ dom = int(id) if dom <= 0: @@ -292,15 +402,26 @@ class XendDomain: val = dominfo.destroy() else: val = xc.domain_destroy(dom=dom) - if refresh: self.refresh() return val + def domain_destroy(self, id): + """Terminate domain immediately. + Camcels any restart for the domain. + + id domain id + """ + self.domain_restart_cancel(id) + val = self.final_domain_destroy(id) + self.refresh_schedule() + return val + def domain_migrate(self, id, dst): """Start domain migration. id domain id """ # Need a cancel too? + # Don't forget to cancel restart for it. pass def domain_save(self, id, dst, progress=0): diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index 869fc28e07..f14e6477c2 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -362,6 +362,8 @@ class XendDomainInfo: self.state = self.STATE_OK #todo: set to migrate info if migrating self.migrate = None + #Whether to auto-restart + self.autorestart = 0 def setdom(self, dom): self.dom = int(dom) @@ -418,6 +420,8 @@ class XendDomainInfo: try: self.name = sxp.child_value(config, 'name') self.memory = int(sxp.child_value(config, 'memory', '128')) + if sxp.child(config, 'autorestart', None): + self.autorestart = 1 self.configure_backends() image = sxp.child_value(config, 'image') image_name = sxp.name(image) diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py index 156198bd70..085dd2642e 100644 --- a/tools/python/xen/xend/server/SrvDomain.py +++ b/tools/python/xen/xend/server/SrvDomain.py @@ -27,7 +27,11 @@ class SrvDomain(SrvDir): return val def op_shutdown(self, op, req): - val = self.xd.domain_shutdown(self.dom.id) + #val = self.xd.domain_shutdown(self.dom.id) + fn = FormFn(self.xd.domain_shutdown, + [['dom', 'int'], + ['reason', 'str']]) + val = fn(req.args, {'dom': self.dom.id}) req.setResponseCode(202) req.setHeader("Location", "%s/.." % req.prePathURL()) return val diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py index 36bcddef92..3c61db9325 100644 --- a/tools/python/xen/xm/create.py +++ b/tools/python/xen/xm/create.py @@ -101,6 +101,10 @@ gopts.opt('memory', short='m', val='MEMORY', fn=set_value, default=128, use="Domain memory in MB.") +gopts.opt('autorestart', + fn=set_true, default=0, + use="Whether to restart the domain on exit.") + gopts.opt('blkif', fn=set_true, default=0, use="Make the domain a block device backend.") @@ -271,6 +275,8 @@ def make_config(opts): config.append(['backend', ['blkif']]) if opts.netif: config.append(['backend', ['netif']]) + if opts.autorestart: + config.append(['autorestart']) configure_image(config, opts) config_devs = [] diff --git a/tools/python/xen/xm/shutdown.py b/tools/python/xen/xm/shutdown.py index aaa354554a..926aaf27f3 100644 --- a/tools/python/xen/xm/shutdown.py +++ b/tools/python/xen/xm/shutdown.py @@ -1,4 +1,5 @@ # Copyright (C) 2004 Mike Wray + """Domain shutdown. """ import string @@ -10,7 +11,8 @@ from xen.xm.opts import * gopts = Opts(use="""[options] [DOM] -Shutdown one or more domains gracefully.""") +Shutdown one or more domains gracefully. +""") gopts.opt('help', short='h', fn=set_true, default=0, @@ -24,18 +26,22 @@ gopts.opt('wait', short='w', fn=set_true, default=0, use='Wait for shutdown to complete.') -gopts.opt('norestart', short='n', +gopts.opt('halt', short='H', + fn=set_true, default=0, + use='Shutdown without reboot.') + +gopts.opt('reboot', short='R', fn=set_true, default=0, - use='Prevent domain restart.') + use='Shutdown and reboot.') -def shutdown(opts, doms, wait): +def shutdown(opts, doms, mode, wait): def domains(): return [ int(a) for a in server.xend_domains() ] if doms == None: doms = domains() if 0 in doms: doms.remove(0) for d in doms: - server.xend_domain_shutdown(d) + server.xend_domain_shutdown(d, mode) if wait: while doms: alive = domains() @@ -49,6 +55,21 @@ def shutdown(opts, doms, wait): time.sleep(1) opts.info("All domains terminated") +def shutdown_mode(opts): + mode = 'poweroff' + if opts.vals.wait: + mode = 'halt' + if opts.vals.reboot: + opts.err("Can't specify wait and reboot") + else: + if opts.vals.halt and opts.vals.reboot: + opts.err("Can't specify halt and reboot") + if opts.vals.halt: + mode = 'halt' + elif opts.vals.reboot: + mode = 'reboot' + return mode + def main_all(opts, args): shutdown(opts, None, opts.vals.wait) @@ -59,7 +80,9 @@ def main_dom(opts, args): domid = int(dom) except: opts.err('Invalid domain: ' + dom) - shutdown(opts, [ domid ], opts.vals.wait) + + mode = shutdown_mode(opts) + shutdown(opts, [ domid ], mode, opts.vals.wait) def main(argv): opts = gopts -- 2.30.2